use actix_web::http::StatusCode;
use actix_web::{HttpResponse, ResponseError};
use diesel::result::Error as DieselError;
use serde::Deserialize;
use serde_json::json;
use std::fmt;

#[derive(Debug, Deserialize)]
pub struct CustomError {
    pub error_status_code: u16,
    pub error_message: String,
}

impl CustomError {
    pub fn new(error_status_code: u16, error_message: String) -> CustomError {
        CustomError {
            error_status_code,
            error_message,
        }
    }
}

impl fmt::Display for CustomError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.error_message.as_str())
    }
}

impl From<DieselError> for CustomError {
    fn from(error: DieselError) -> CustomError {
        match error {
            DieselError::DatabaseError(_, err) => CustomError {
                error_status_code: 409,
                error_message: err.message().to_string(),
            },
            DieselError::NotFound => CustomError {
                error_status_code: 404,
                error_message: "Eployee record not found".to_string(),
            },

            err => CustomError {
                error_status_code: 500,
                error_message: format!("Unknow Diesel error:{}", err),
            },
        }
    }
}

impl ResponseError for CustomError {
    fn error_response(&self) -> HttpResponse {
        // match code
        let status_code = match StatusCode::from_u16(self.error_status_code) {
            Ok(status_code) => status_code,
            Err(_) => StatusCode::INTERNAL_SERVER_ERROR,
        };

        // match message
        let error_message = match status_code.as_u16() < 500 {
            true => self.error_message.clone(),
            false => "Server internal error!".to_string(),
        };

        HttpResponse::build(status_code).json(json!({ "message": error_message }))
    }
}
